// packet-splitter.js
/**
 * 蓝牙数据分包处理器
 * 功能：大数据分片发送、接收重组、传输校验
 */

import { Bluetooth } from './core';
import { createCRCChecker } from './crc-checker';

// 添加协议头：版本(1B) + 总包数(2B) + CRC(2B)
const HEADER_SIZE = 5;
export interface PacketSplitterOptions {
  chunkSize?: number; // 分片大小（字节）
  maxRetries?: number; // 分片发送失败重试次数
  timeout?: number; // 单分片超时时间(ms)
  crcChecker?: ReturnType<typeof createCRCChecker>; // CRC校验器
}

export class PacketSplitter {
  public config: Required<PacketSplitterOptions>;
  public receiveBuffer: {
    packets: Map<string, ArrayBuffer[]>; // deviceId -> packet[]
    timers: Map<string, NodeJS.Timeout>; // 重组超时定时器
    expectedSequences: Map<string, number>; // 预期序列号
  };
  constructor({ crcChecker, ...rest }: PacketSplitterOptions = {}) {
    // 配置参数
    this.config = {
      chunkSize: 20,
      maxRetries: 3,
      timeout: 2000,
      ...rest,
      crcChecker: crcChecker || createCRCChecker('CRC16-CCITT')
    };

    // 接收缓冲区
    this.receiveBuffer = {
      packets: new Map(), // deviceId -> packet[]
      timers: new Map(), // 重组超时定时器
      expectedSequences: new Map() // 预期序列号
    };
  }

  // 数据分片发送
  async send(
    bluetooth: Bluetooth,
    deviceId: string,
    serviceId: string,
    characteristicId: string,
    data: ArrayBuffer
  ): Promise<void> {
    if (!(data instanceof ArrayBuffer)) {
      data = new Uint8Array(data).buffer;
    }
    const maxDataSize = this.config.chunkSize - HEADER_SIZE;
    const totalPackets = Math.ceil(data.byteLength / maxDataSize);

    // 生成分包
    const chunks: ArrayBuffer[] = [];
    const dataView = new DataView(data);
    const crc = this.config.crcChecker.calculate(data);

    for (let i = 0; i < totalPackets; i++) {
      const offset = i * maxDataSize;
      const chunkDataSize = Math.min(maxDataSize, data.byteLength - offset);
      const packet = new ArrayBuffer(this.config.chunkSize);
      const packetView = new DataView(packet);

      // 填充协议头
      packetView.setUint8(0, 0x01); // 协议版本
      packetView.setUint16(1, totalPackets, true); // 小端序
      packetView.setUint16(3, crc, true);

      // 填充数据
      for (let j = 0; j < chunkDataSize; j++) {
        packetView.setUint8(HEADER_SIZE + j, dataView.getUint8(offset + j));
      }

      chunks.push(packet);
    }

    // 分片发送（带重试）
    for (let i = 0; i < chunks.length; i++) {
      await this._sendWithRetry(bluetooth, deviceId, serviceId, characteristicId, chunks[i], i);
    }
  }

  /**
   * 分片发送（带重试机制）
   */
  async _sendWithRetry(
    bluetooth: Bluetooth,
    deviceId: string,
    serviceId: string,
    characteristicId: string,
    packet: ArrayBuffer,
    sequence: number
  ) {
    let retryCount = 0;
    const { maxRetries, timeout } = this.config;

    while (retryCount <= maxRetries) {
      try {
        // 设置超时控制
        const timeoutPromise = new Promise((_, reject) => {
          setTimeout(() => reject(new Error('发送超时')), timeout);
        });

        // 并行执行写入和超时监控
        await Promise.race([bluetooth.write(serviceId, characteristicId, packet), timeoutPromise]);

        console.log(`分片 ${sequence} 发送成功`);
        return;
      } catch (err) {
        if (retryCount++ >= maxRetries) {
          throw new Error(`分片 ${sequence} 发送失败: ${err.message}`);
        }
        console.warn(`分片 ${sequence} 第 ${retryCount} 次重试...`);
        await new Promise((r) => setTimeout(r, 200 * retryCount)); // 退避延迟
      }
    }
  }

  /**
   * 处理接收到的分片数据
   * @param {ArrayBuffer} data
   * @param {string} deviceId
   * @returns {ArrayBuffer|null} 完整数据包或null(未完成)
   */
  processReceivedPacket(data: ArrayBuffer, deviceId: string, crcChecked = false): ArrayBuffer | null {
    let validatedData = data;
    if (!crcChecked) {
      const { data: _validatedData, valid } = this.config.crcChecker.validatePacket(data);
      if (!valid) {
        throw new Error('CRC校验失败');
      }
      validatedData = _validatedData;
    }
    const view = new DataView(validatedData);
    // 解析协议头
    // const version = view.getUint8(0);
    const totalPackets = view.getUint16(1, true);
    const sequence = this._getPacketSequence(validatedData);

    // 初始化接收缓冲区
    if (!this.receiveBuffer.packets.has(deviceId)) {
      this.receiveBuffer.packets.set(deviceId, new Array(totalPackets));
      this.receiveBuffer.expectedSequences.set(deviceId, 0);
      this._setReassemblyTimeout(deviceId);
    }

    const buffer = this.receiveBuffer.packets.get(deviceId) || [];
    buffer[sequence] = validatedData;

    // 检查是否全部接收完成
    if (buffer.every((p) => p !== undefined)) {
      this._clearTimeout(deviceId);
      return this._assemblePackets(buffer);
    }

    this.receiveBuffer.expectedSequences.set(deviceId, sequence + 1);
    return null;
  }

  /**
   * 重组数据包
   */
  _assemblePackets(packets: ArrayBuffer[]) {
    // 计算总数据大小
    let totalSize = 0;
    packets.forEach((arrayBuffer) => {
      totalSize += arrayBuffer.byteLength - HEADER_SIZE;
    });

    // 合并数据
    const result = new Uint8Array(totalSize);
    let offset = 0;

    packets.forEach((p) => {
      const view = new DataView(p);
      const chunkSize = p.byteLength - HEADER_SIZE;

      for (let i = 0; i < chunkSize; i++) {
        result[offset++] = view.getUint8(HEADER_SIZE + i);
      }
    });

    return result.buffer;
  }

  /**
   * 设置重组超时定时器
   */
  _setReassemblyTimeout(deviceId: string) {
    this._clearTimeout(deviceId);
    const timer = setTimeout(() => {
      this.receiveBuffer.packets.delete(deviceId);
      this.receiveBuffer.expectedSequences.delete(deviceId);
      console.warn(`设备 ${deviceId} 数据包重组超时`);
    }, this.config.timeout * 3); // 超时时间是单分片的3倍
    this.receiveBuffer.timers.set(deviceId, timer);
  }

  _clearTimeout(deviceId: any) {
    if (this.receiveBuffer.timers.has(deviceId)) {
      clearTimeout(this.receiveBuffer.timers.get(deviceId));
      this.receiveBuffer.timers.delete(deviceId);
    }
  }

  /**
   * CRC-16/CCITT 校验计算
   */
  _calculateCRC(data: ArrayBuffer) {
    const view = new Uint8Array(data);
    let crc = 0xffff;

    for (const byte of view) {
      crc ^= byte << 8;
      for (let i = 0; i < 8; i++) {
        crc = crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1;
      }
    }

    return crc & 0xffff;
  }

  /**
   * 从数据包中提取序列号（自定义算法）
   */
  _getPacketSequence(data: ArrayBuffer) {
    // 简单实现：使用最后一个字节作为序列号
    return new Uint8Array(data)[data.byteLength - 1] % 256;
  }
}

export function createPacketSplitter(options?: PacketSplitterOptions) {
  return new PacketSplitter(options);
}
